package config

import (
	"crypto/aes"
	"crypto/cipher"
	"crypto/md5"
	"crypto/rand"
	"encoding/base64"
	"encoding/hex"
	"encoding/json"
	"end/entry"
	"fmt"
	"github.com/aliyun/aliyun-oss-go-sdk/oss"
	"github.com/dgrijalva/jwt-go"
	"gorm.io/driver/mysql"
	"gorm.io/gorm"
	"gorm.io/gorm/logger"
	"gorm.io/gorm/schema"
	"io"
	"reflect"
	"strconv"
	"sync"
	"time"
)

type UserIdentity struct {
	Id       int
	Admin    entry.Admin    `gorm:"foreignkey:UserID"`
	Clerk    entry.Clerk    `gorm:"foreignkey:UserID"`
	Business entry.Business `gorm:"foreignkey:UserID"`
}

func (u UserIdentity) String() string {
	return fmt.Sprintf("ID: %d\nAdmin: %v\nClerk: %v\nBusiness: %v", u.Id, u.Admin, u.Clerk, u.Business)
}

func (UserIdentity) TableName() string {
	return "user"
}

type Additional struct {
	Type string `json:"type"`
	Id   int    `json:"id"`
}

type AllInfo struct {
	UserId      int          `json:"userid"`
	Level       int          `json:"level"`
	Additionals []Additional `json:"additionals"`
}

type UserInfo struct {
	Id       int
	Identity string
}

var (
	db           *gorm.DB
	once         sync.Once
	mySigningKey = []byte("147258369abc")
	lock         sync.Mutex
	aesKey       = []byte("1234567890123456")
)

func Lock() {
	lock.Lock()
}

func Unlock() {
	lock.Unlock()
}

// STATUS1 待审核
const STATUS1 = 0b000010

// STATUS2 已通过
const STATUS2 = 0b000100

// STATUS3 已经驳回
const STATUS3 = 0b001000

// STATUS6 超时
const STATUS6 = 0b1000000

const ADMIN1 = 0b00001
const ADMIN2 = 0b00010
const ADMIN3 = 0b00100
const CLERK = 0b01000
const BUSINESS = 0b10000

func GetDb() *gorm.DB {
	once.Do(func() {
		dsn := "xiaochengxu:TXpEpH6DA7j2Mck2@tcp(43.136.35.11:3306)/xiaochengxu?charset=utf8mb4&parseTime=True&loc=Local"
		//dsn := "xiaochengxu:TXpEpH6DA7j2Mck2@tcp(127.0.0.1:3306)/xiaochengxu?charset=utf8mb4&parseTime=True&loc=Local"
		var err error
		db, err = gorm.Open(mysql.Open(dsn), &gorm.Config{
			NamingStrategy: schema.NamingStrategy{
				SingularTable: true,
			},
			Logger: logger.Default.LogMode(logger.Info),
		})
		if err != nil {
			panic("failed to connect database")
		}
	})
	return db
}

// TestDb 测试连接是否成功，这个会打印全部的表格
func TestDb() {
	var tables []string
	db := GetDb()
	db.Raw("SHOW TABLES").Scan(&tables)

	// 打印表格名称
	for _, table := range tables {
		fmt.Println(table)
	}
}

func DetectionUser(userid int) AllInfo {
	var userIdentity UserIdentity
	db.Preload("Admin").
		Preload("Clerk").
		Preload("Business").
		Where("id = ?", userid).
		First(&userIdentity)
	if userIdentity.Id == 0 {
		return AllInfo{
			UserId: userid,
		}
	}
	var additionals []Additional
	allInfo := AllInfo{
		UserId:      userid,
		Level:       0,
		Additionals: additionals,
	}

	if userIdentity.Admin.ID != 0 {
		if userIdentity.Admin.Power == 1 {
			allInfo.Level |= ADMIN1
		} else if userIdentity.Admin.Power == 2 {
			allInfo.Level |= ADMIN2
		} else if userIdentity.Admin.Power == 3 {
			allInfo.Level |= ADMIN3
		}
		additionals = append(additionals, Additional{
			Type: "admin" + strconv.Itoa(userIdentity.Admin.Power),
			Id:   userIdentity.Admin.ID,
		})
	}

	if userIdentity.Business.ID != 0 && userIdentity.Business.Status != STATUS1 {
		allInfo.Level |= BUSINESS
		additionals = append(additionals, Additional{
			Type: "business",
			Id:   userIdentity.Business.ID,
		})
	}

	if userIdentity.Clerk.ID != 0 {
		allInfo.Level |= CLERK
		additionals = append(additionals, Additional{
			Type: "clerk",
			Id:   userIdentity.Clerk.ID,
		})
	}
	allInfo.Additionals = additionals
	return allInfo
}

// CreateToken 创建token
func CreateToken(userid int) (string, []Additional) {
	token := jwt.New(jwt.SigningMethodHS256)
	claims := token.Claims.(jwt.MapClaims)
	// 解析
	allInfo := DetectionUser(userid)
	jsonData, err := json.Marshal(allInfo)
	if err != nil {
		return "", nil
	}
	encrypt, err := Encrypt(string(jsonData))
	if err != nil {
		return "", nil
	}
	claims["exp"] = time.Now().AddDate(0, 0, 30).Unix()
	// 加密
	claims["allInfo"] = encrypt
	// jwt加密
	tokenString, err := token.SignedString(mySigningKey)
	if err != nil {
		return "", nil
	}
	return tokenString, allInfo.Additionals
}

// AnalysisToken 解析token，同时返回解析值，类型和这个类型的id
func AnalysisToken(tokenString string) AllInfo {
	token, err := jwt.Parse(tokenString, func(token *jwt.Token) (interface{}, error) {
		// 确保令牌的方法符合预期
		if _, ok := token.Method.(*jwt.SigningMethodHMAC); !ok {
			return nil, fmt.Errorf("unexpected signing method: %v", token.Header["alg"])
		}
		return mySigningKey, nil
	})
	// 解码失败
	if err != nil {
		fmt.Printf("error: %v\n", err)
		return AllInfo{} // 返回一个空的UserInfo
	}
	claims, ok := token.Claims.(jwt.MapClaims)
	// 解析失败
	if !(ok && token.Valid) {
		return AllInfo{}
	}
	// 信息错误
	info, ok := claims["allInfo"].(string)
	if !ok {
		return AllInfo{} // 无法提取info，返回空的UserInfo
	}
	// 超时，返回空的
	exp := int64(claims["exp"].(float64))
	if (time.Now().Unix() - exp) > 0 {
		return AllInfo{}
	}
	// 解密
	decrypt, err := Decrypt(info)
	if err != nil {
		return AllInfo{}
	}
	var allInfo AllInfo
	err = json.Unmarshal([]byte(decrypt), &allInfo)
	if err != nil {
		fmt.Println("JSON 解码失败:", err)
		return AllInfo{}
	}
	return allInfo
}

// UploadFile 上传文件
func UploadFile(path string, filename string) string {
	client, err := oss.New("oss-cn-shenzhen.aliyuncs.com",
		"LTAI5t7nyCXvG3jVyu73QcxS", "qjm2Y0smF9QkoRnMShOYHrLglnaimI")
	if err != nil {
		return ""
	}

	bucket, err := client.Bucket("baishitong73")
	if err != nil {
		return ""
	}
	// 哈希值
	hashes := md5.New()
	hashes.Write([]byte(filename))
	hashInBytes := hashes.Sum(nil)
	name := hex.EncodeToString(hashInBytes)
	err = bucket.PutObjectFromFile(name, path)
	if err != nil {
		return ""
	}
	return "https://baishitong73.oss-cn-shenzhen.aliyuncs.com/" + name
}

// DeleteFile 删除文件
func DeleteFile() int {
	// 返回1删除成功
	return 1
}

// UpdateToMap 使用反射将结构体转换为 map[string]interface{}
func UpdateToMap(model interface{}) (map[string]interface{}, error) {
	valueOf := reflect.ValueOf(model)

	// 确保传入的是指针类型
	if valueOf.Kind() != reflect.Ptr || valueOf.Elem().Kind() != reflect.Struct {
		return nil, fmt.Errorf("model must be a pointer to a struct")
	}

	// 获取结构体类型信息
	structType := valueOf.Elem().Type()

	// 创建 map 用于存储非空字段
	updates := make(map[string]interface{})

	// 遍历结构体字段
	for i := 0; i < structType.NumField(); i++ {
		field := structType.Field(i)
		fieldName := field.Name

		// 检查字段的标签，如果存在则使用标签值作为字段名，判断是否存在json
		tagValue, ok := field.Tag.Lookup("gorm")
		if ok && tagValue != "-" {
			fieldName = tagValue
		}

		// 获取字段值
		fieldValue := valueOf.Elem().Field(i).Interface()

		// 如果字段值非空，则加入更新列表
		if !reflect.DeepEqual(fieldValue, reflect.Zero(field.Type).Interface()) {
			updates[fieldName] = fieldValue
		}
	}

	return updates, nil
}

// Encrypt 加密函数
func Encrypt(text string) (string, error) {
	plaintext := []byte(text)

	// 使用 PKCS7 填充方式对 plaintext 进行填充
	padding := aes.BlockSize - (len(plaintext) % aes.BlockSize)
	for i := 0; i < padding; i++ {
		plaintext = append(plaintext, byte(padding))
	}

	block, err := aes.NewCipher(aesKey)
	if err != nil {
		return "", err
	}

	ciphertext := make([]byte, aes.BlockSize+len(plaintext))
	iv := ciphertext[:aes.BlockSize]
	if _, err := io.ReadFull(rand.Reader, iv); err != nil {
		return "", err
	}

	mode := cipher.NewCBCEncrypter(block, iv)
	mode.CryptBlocks(ciphertext[aes.BlockSize:], plaintext)

	return base64.URLEncoding.EncodeToString(ciphertext), nil
}

// Decrypt 解密函数
func Decrypt(ciphertext string) (string, error) {
	decoded, err := base64.URLEncoding.DecodeString(ciphertext)
	if err != nil {
		return "", err
	}

	block, err := aes.NewCipher(aesKey)
	if err != nil {
		return "", err
	}

	if len(decoded) < aes.BlockSize {
		return "", fmt.Errorf("ciphertext is too short")
	}

	iv := decoded[:aes.BlockSize]
	decoded = decoded[aes.BlockSize:]

	mode := cipher.NewCBCDecrypter(block, iv)
	mode.CryptBlocks(decoded, decoded)

	// 去除填充字节
	padding := int(decoded[len(decoded)-1])
	if padding < 1 || padding > aes.BlockSize {
		return "", fmt.Errorf("invalid padding")
	}

	decoded = decoded[:len(decoded)-padding]

	return string(decoded), nil
}
